package manager.match;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.security.auth.x500.X500Principal;

import org.apache.log4j.Logger;
import org.springframework.beans.BeansException;
import org.springframework.context.event.EventListenerMethodProcessor;

import com.alibaba.dubbo.rpc.cluster.support.FailbackCluster;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.deser.DataFormatReaders.Match;

import advice.ExceptionManager;
import database.DBService;
import database.RecordFileDBServer;
import database.pojo.RecordFileInfo;
import io.netty.channel.ChannelFuture;
import manager.connection.ClientConnectionManager;
import manager.match.basic.state.EnrollPlayerState;
import manager.match.basic.state.MatchState;
import manager.match.basic.type.EnrollPlayerInfo;
import manager.match.basic.type.MatchInfo;
import manager.match.basic.type.MatchScore;
import manager.player.PlayerManager;
import manager.player.basic.state.GeneralPlayerState;
import net.socket.server.notification.MatchStateChangedNTF;
import net.socket.server.notification.TranspondSore;
import net.socket.server.protocal.PROTOID;
import net.socket.server.request.ChallengePlayerRequest;
import net.socket.server.request.ChangeAvatarRequest;
import net.socket.server.request.EnrollRequest;
import net.socket.server.request.FightFinishRequest;
import net.socket.server.request.GetMatchInfoRequest;
import net.socket.server.request.GetRandomEnrollListRequest;
import net.socket.server.request.IRequest;
import net.socket.server.request.LeaveFightRequest;
import net.socket.server.request.MatchChoiceRequest;
import net.socket.server.request.P2PConnectionReadyRequest;
import net.socket.server.request.PlayerLogoutRequest;
import net.socket.server.request.UploadRecordRequest;
import net.socket.server.response.ChallengePlayerResponse;
import net.socket.server.response.ChangeAvatarResponse;
import net.socket.server.response.FightFinishResponse;
import net.socket.server.response.GetMatchInfoResponse;
import net.socket.server.response.GetRandomMEnrollListResponse;
import net.socket.server.response.LeaveFightResponse;
import net.socket.server.response.MatchChoiceResponse;
import net.socket.server.response.P2PConnectionReadyResponse;
import net.socket.server.response.PlayerLogoutResponse;
import net.socket.server.response.QuitEnrollResponse;
import net.socket.server.response.UploadRecordResponse;
import observable.subject.MessageCenter;
import util.MyFactory;
import util.ResultTypeList;

public class MatchManager {
	// params are : key - match id, value - match info
	private Map<Integer, MatchInfo> matchList = new HashMap<Integer, MatchInfo>();
	private static final long WAIT_FOR_READY_OVERTIME = 10000; //10s
	private static final long WAIT_FOR_CONNECTION_OVERTIME = 20000; 
	private static final long WAIT_FOR_ALL_FINISHED_OVERTIME = 20000; 
	private static final long MAX_SONG_TIME = 10 * 60 * 1000; 
	private static final long WAIT_FOR_PLAYER_LEAVE_OVERTIME = 5 * 60 * 1000;
	
	private ClientConnectionManager ccm = null;
	private PlayerManager pm = null;
	private EnrollmentManager em = null;
	private static Logger logger = Logger.getLogger(ExceptionManager.class);

	private static final long CLEAN_PERIOD = 30000;
	private long lastClean = System.currentTimeMillis();
	
	public void init(){
		MessageCenter mc = MyFactory.context.getBean("MessageCenter", MessageCenter.class);
		mc.register(PROTOID.MATCH_CHOICE_REQ, (int cmd, IRequest request)->handleMatchChoice(cmd, request));
		mc.register(PROTOID.P2P_CONNECTION_READY_REQ, (int cmd, IRequest request)->handleP2PConnectionReady(cmd, request));
		mc.register(PROTOID.FIGHT_FINISH_REQ, (int cmd, IRequest request)->handleFightFinish(cmd, request));
		mc.register(PROTOID.LEAVE_FIGHT_REQ, (int cmd, IRequest request)->handleLeaveFight(cmd, request));
		mc.register(PROTOID.HEART_BEAT_ONE_TENTH_SECOND, (int cmd, IRequest request)->onTimer(cmd, request));
		mc.register(PROTOID.CHANGE_AVATAR_REQ, (int cmd, IRequest request)->handleAvatar(cmd, request));
		mc.register(PROTOID.GET_MATCH_INFO_REQ, (int cmd, IRequest request)->getMatchInfo(cmd, request));
		mc.register(PROTOID.UPLOAD_RECORD_REQ, (int cmd, IRequest request)->uploadRecord(cmd, request));
		mc.register(PROTOID.REQ_TRANSPOND_SOCRE, (int cmd, IRequest request)->transpondSocre(cmd, request));
		
		em = MyFactory.context.getBean("EnrollmentManager", EnrollmentManager.class);
		ccm = MyFactory.context.getBean("ClientConnectionManager", ClientConnectionManager.class);
		pm = MyFactory.context.getBean("PlayerManager", PlayerManager.class);
		
	}
	
	private int transpondSocre(int cmd, IRequest request) throws NullPointerException,IllegalArgumentException, IllegalAccessException, JsonProcessingException {
		TranspondSore req=(TranspondSore)request;
		//System.out.println("openId:"+req.getOpenId());
		if(ccm.getOpenIdByMachine(req.getOpponentMID()).equals(req.getOpenId())) {
			ccm.sendResponse(PROTOID.REQ_TRANSPOND_SOCRE, req.getOpponentMID(), req);
		}
		return 0;
	}

	private int uploadRecord(int cmd, IRequest request) throws IllegalArgumentException, IllegalAccessException, JsonProcessingException {
		UploadRecordRequest req =(UploadRecordRequest)request;
		UploadRecordResponse rsp = new UploadRecordResponse();
		if("".equals(req.getOpenId())||"".equals(req.getSongId())) {
			rsp.setResult(ResultTypeList.NULL_ARGS);
			ccm.sendResponse(PROTOID.UPLOAD_RECORD_RSP, req.getMachineId(), rsp);
			return ResultTypeList.NULL_ARGS;
		}
		RecordFileInfo recordInfo = new RecordFileInfo();
		ChannelFuture cf =null;
		/*只需插入
		 * fileName, songId, status, fileType, uid,duration,score
		 * 其中status =-1表示文件还没有上传成功，fileType=0表示mp3，上传成功后会在fileserver里面更新表
		 */
		
		try {
			recordInfo.setUid(MyFactory.context.getBean("DBService", DBService.class).getUidByOpenId(req.getOpenId()));
		} catch (BeansException | SQLException e) {
			System.out.println("uploadRecord getUidByOpenId Exception:"+e);
			rsp.setResult(ResultTypeList.SELECT_DB_EXCEPTION);
			cf = ccm.sendResponse(PROTOID.UPLOAD_RECORD_RSP, req.getMachineId(), rsp);
			return ResultTypeList.SELECT_DB_EXCEPTION;
		}
		recordInfo.setDuration(req.getDuration());
		recordInfo.setScore(req.getScore());
		recordInfo.setSongId(req.getSongId());
		recordInfo.setStatus(-1);
		recordInfo.setFileType(0);
		recordInfo.setSingTime(req.getSingTime());
		String fileName =System.currentTimeMillis() + Integer.toString(recordInfo.getUid()) +".mp3";//文件名以时间戳和uid命名保证唯一性
		recordInfo.setFileName(fileName);
		try {
			if(MyFactory.context.getBean("RecordFileDBServer", RecordFileDBServer.class).insert(recordInfo)) {
				rsp.setResult(ResultTypeList.SUCCESS);
				rsp.setFileName(fileName);
			}else {
				rsp.setResult(ResultTypeList.INSERT_DB_EXCEPTION);
			}
			cf = ccm.sendResponse(PROTOID.UPLOAD_RECORD_RSP, req.getMachineId(), rsp);
			return ResultTypeList.INSERT_DB_EXCEPTION;
		} catch (BeansException | SQLException e) {
			System.out.println("uploadRecord insert Exception:"+e);
			rsp.setResult(ResultTypeList.INSERT_DB_EXCEPTION);
			cf = ccm.sendResponse(PROTOID.UPLOAD_RECORD_RSP, req.getMachineId(), rsp);
			return ResultTypeList.INSERT_DB_EXCEPTION;
		}
	}

	private int getMatchInfo(int cmd, IRequest request) throws IllegalArgumentException, IllegalAccessException, JsonProcessingException {
		GetMatchInfoRequest req = (GetMatchInfoRequest)request;
		GetMatchInfoResponse rsp = new GetMatchInfoResponse();
		ChannelFuture cf =null;
		if("".equals(req.getOpenId())) {
			rsp.setResult(ResultTypeList.NULL_ARGS);
			ccm.sendResponse(PROTOID.GET_MATCH_INFO_RSP, req.getMachineId(), rsp);
			return ResultTypeList.NULL_ARGS;
		}
		MatchInfo matchInfo = getMatchInfoByOpenId(req.getOpenId());
		if(matchInfo == null) {
			rsp.setResult(ResultTypeList.UNKNOWN_FAIL);
			//System.out.println("no found MatchInfo");
			logger.info("no found MatchInfo");
		}else {
			rsp.setResult(ResultTypeList.SUCCESS);
			rsp.setMatchInfo(matchInfo);
			//System.out.println("found MatchInfo");
		}
		cf = ccm.sendResponse(PROTOID.GET_MATCH_INFO_RSP, req.getMachineId(), rsp);
		if(cf == null) {
			return ResultTypeList.NO_CONNECTION_FOUND;
		}else {
			return ResultTypeList.SUCCESS;
		}
	}
	public MatchInfo getMatchInfoByOpenId(String openId) {
		//MatchInfo player = null;
		MatchInfo returnplayer = null;
		for(MatchInfo player :matchList.values()) {
			List<EnrollPlayerInfo> list = player.getPlayerList();
			if(list != null){
				Iterator<EnrollPlayerInfo> it = list.iterator();
				while(it.hasNext()) {
					if(it.next().getPlayerInfo().getOpenId().equals(openId)) {
						returnplayer =player;
						return returnplayer;
					}
				}
		}
		
	}
		return returnplayer;
}

	private int handleAvatar(int cmd, IRequest request) throws IllegalArgumentException, IllegalAccessException, JsonProcessingException{
		//System.out.println("--------------handleAvatar  ");
		ChangeAvatarRequest req =(ChangeAvatarRequest) request;
		ChangeAvatarResponse rsp = new ChangeAvatarResponse();
		if("".equals(req.getOpenId())){
			rsp.setResult(ResultTypeList.NULL_ARGS);
		}else {
			try {
				MyFactory.context.getBean("DBService", DBService.class).updateAvatarId(req.getAvatarId(), req.getOpenId());
				rsp.setResult(ResultTypeList.SUCCESS);
				rsp.setAvatarId(req.getAvatarId());
			} catch (BeansException | SQLException e) {
				e.printStackTrace();
				rsp.setResult(ResultTypeList.UPDATA_DB_EXCEPTION);
			}
		}
		ChannelFuture cf = ccm.sendResponse(PROTOID.CHANGE_AVATAR_RSP, req.getMachineId(), rsp);
		pm.setPlayerAvatarId(req.getOpenId(), req.getAvatarId());
		if(cf == null)
			return ResultTypeList.NO_CONNECTION_FOUND;
		else {
			return rsp.getResult();
		}
	}

	public int onTimer(int cmd, IRequest request) throws IllegalArgumentException, IllegalAccessException, JsonProcessingException{
		// check for match overtime
		if(matchList.isEmpty()) {
			return 0;
		}
		MatchInfo match =null;
		Iterator<Map.Entry<Integer, MatchInfo>> it = matchList.entrySet().iterator();
		long duration =0;
		while(it.hasNext()) {			
			match=it.next().getValue();
			duration = System.currentTimeMillis() - match.getStartTime();
			if(match.getState() == MatchState.WAIT_FOR_READY&&duration > WAIT_FOR_READY_OVERTIME){
					//System.out.println("wait for ready overtime, remove match: " + match.getMatchId());
					logger.info("wait for ready overtime, remove match: " + match.getMatchId());
					it.remove();
					waitForReadyOvertime(match);
			}else if(match.getState() == MatchState.WAIT_FOR_CONNECTION&&duration > WAIT_FOR_CONNECTION_OVERTIME) {
				//System.out.println("wait for connection overtime, remove match: " + match.getMatchId());
				logger.info("wait for connection overtime, remove match: " + match.getMatchId());
				it.remove();
				waitForConnectionOvertime(match);
			}else if(match.getState() == MatchState.FIGHTING){
				waitForFinishFightingOverTime(match);
			}else if(match.getState() == MatchState.FIRST_ONE_FINISHED){
				waitForAllFinished(match);
			}else if(match.getState() == MatchState.FINISHED&&duration > WAIT_FOR_PLAYER_LEAVE_OVERTIME) {
				//System.out.println("wait for leave room overtime, finish match: " + match.getMatchId());
				logger.info("wait for leave room overtime, finish match: " + match.getMatchId());
				waitForPlayerLeaveRoom(match);
				it.remove();
			}
		}
		long durationLast = System.currentTimeMillis() - lastClean;
		if(durationLast > CLEAN_PERIOD)
			cleanMatchList();
		
		return cmd;
	}
	
	private void cleanMatchList(){
		Iterator<Map.Entry<Integer, MatchInfo>> it = matchList.entrySet().iterator();  
        while(it.hasNext()){  
            Map.Entry<Integer, MatchInfo> entry = it.next();  
            if(entry.getValue().isToBeRemoved())  
                it.remove();//使用迭代器的remove()方法删除元素  
        }  
        lastClean = System.currentTimeMillis();
	}
	
	//处理等待超时
	private void waitForReadyOvertime(MatchInfo match) throws IllegalArgumentException, IllegalAccessException, JsonProcessingException{
		changeMatchStatus(match);
		match.setState(MatchState.MATCH_FAILURE);
		MatchStateChangedNTF notif = getNotifByMatch(match);
		for (EnrollPlayerInfo player : match.getPlayerList()) {
			ccm.sendResponse(PROTOID.MATCH_STATE_CHANGED_NTF, player.getMachineId(), notif);
			if(player.getState() == EnrollPlayerState.READY){
					
				//get a new match for the refused people
				EnrollPlayerInfo opponent = em.getMatchedPlayer(player.getSongId(),player.getPlayerInfo().getOpenId());
				if(opponent == null){
					em.addPlayerToEnrollList(player.getSongId(), player);
				}else {
					List<EnrollPlayerInfo> list = new ArrayList<EnrollPlayerInfo>();
					list.add(player);
					list.add(opponent);
					if(!createMatch(match.getSongId(), list)){
						// fail to create a match, to be handled in the future;
					}
				}
			}
		}
	}
	
	private void waitForConnectionOvertime(MatchInfo match) throws IllegalArgumentException, IllegalAccessException, JsonProcessingException{
		changeMatchStatus(match);
		match.setState(MatchState.MATCH_FAILURE);
		MatchStateChangedNTF notif = getNotifByMatch(match);
		for (EnrollPlayerInfo player : match.getPlayerList()) {
			ccm.sendResponse(PROTOID.MATCH_STATE_CHANGED_NTF, player.getMachineId(), notif);
			if(player.getState() == EnrollPlayerState.P2P_CONNECTED){
				
				//get a new match for the refused people
				EnrollPlayerInfo opponent = em.getMatchedPlayer(player.getSongId(),player.getPlayerInfo().getOpenId());
				if(opponent == null){
					em.addPlayerToEnrollList(player.getSongId(), player);
				}else {
					List<EnrollPlayerInfo> list = new ArrayList<EnrollPlayerInfo>();
					list.add(player);
					list.add(opponent);
					if(!createMatch(match.getSongId(), list)){
						// fail to create a match, to be handled in the future;
					}
				}
			}
		}
	}
	
	 private void waitForFinishFightingOverTime(MatchInfo match) throws IllegalArgumentException, IllegalAccessException, JsonProcessingException{
		long duration = System.currentTimeMillis() - match.getStartTime();
		if(duration > MAX_SONG_TIME){
			//System.out.println("wait for fighting finish overtime, finish match: " + match.getMatchId());
			logger.info("wait for fighting finish overtime, finish match: " + match.getMatchId());
			finishMatch(match);
			sendMatchStateChangedNTF(match);
		}
	}
	
	private void waitForAllFinished(MatchInfo match) throws IllegalArgumentException, IllegalAccessException, JsonProcessingException{
		long duration = System.currentTimeMillis() - match.getStartTime();
		if(duration > WAIT_FOR_ALL_FINISHED_OVERTIME){
			//System.out.println("wait for finish overtime, finish match: " + match.getMatchId());
			logger.info("wait for finish overtime, finish match: " + match.getMatchId());
			finishMatch(match);
			sendMatchStateChangedNTF(match);
		}
	}
	
	private void waitForPlayerLeaveRoom(MatchInfo match) throws IllegalArgumentException, IllegalAccessException, JsonProcessingException{
			match.setState(MatchState.DISMISSED);
			sendMatchStateChangedNTF(match);
			changeMatchStatus(match);
	}
	
	/*根据报名的玩家信息，创建一场比赛
	 * 将玩家的状态对改成WAITING_FOR_READY
	 * 将比赛状态（一开始创建的比赛状态为WAIT_FOR_READY）通知玩家
	 * 并且将其从匹配列表删除
	 */
	public boolean createMatch(String songId, List<EnrollPlayerInfo> playList) throws IllegalArgumentException, IllegalAccessException, JsonProcessingException{
		MatchInfo match = new MatchInfo();
		for (EnrollPlayerInfo enrollPlayerInfo : playList) {
			enrollPlayerInfo.setState(EnrollPlayerState.WAITING_FOR_READY);
			pm.setPlayerState(enrollPlayerInfo.getPlayerInfo().getOpenId(), GeneralPlayerState.IN_MATCH);
			pm.setPlayerMatchId(enrollPlayerInfo.getPlayerInfo().getOpenId(), match.getMatchId());
			//将玩家从匹配列表删除，并且通知玩家离开匹配列表
			em.removePlayerFromEnrollList(songId, enrollPlayerInfo.getPlayerInfo().getOpenId(),enrollPlayerInfo.getMachineId());
			

		}
		match.setPlayerList(playList);
		match.setSongId(songId);
		insertMatchList(match);
		//System.out.println("match created");
		int result = sendMatchStateChangedNTF(match);//通知玩家比赛状态改变
		if(result == ResultTypeList.SUCCESS)
			return true;
		else {//未能通知玩家，表示，玩家掉线等，但是有一个玩家没有掉线，所以不能删除比赛
			//removeMatch(match.getMatchId());
			return false;
		}
		//return true;
	}
	
	//每当比赛状态信息改变时，将改变发送给所有玩家
	public int sendMatchStateChangedNTF(MatchInfo match) throws IllegalArgumentException, IllegalAccessException, JsonProcessingException{
		//System.out.println("send match changed notif: " + match.getState());
		MatchStateChangedNTF notif = getNotifByMatch(match);
		ChannelFuture cf = null;
		int result = ResultTypeList.SUCCESS;
		for (EnrollPlayerInfo player : match.getPlayerList()) {
			if(ccm.getOpenIdByMachine(player.getMachineId()).equals(player.getPlayerInfo().getOpenId()))
				cf = ccm.sendResponse(PROTOID.MATCH_STATE_CHANGED_NTF, player.getMachineId(), notif);
			else
				System.out.println("the player leaved machine"+player.getPlayerInfo().getOpenId());
			if(cf == null){
				result = ResultTypeList.NO_CONNECTION_FOUND;
				System.out.println("fail to send to machine " + player.getMachineId());
			}
			cf = null;
		}
		return result;
	}
	
	
	//处理玩家对当前匹配的选择
	public int handleMatchChoice(int cmd, IRequest request) throws IllegalArgumentException, IllegalAccessException, JsonProcessingException, BeansException, SQLException{
		MatchChoiceRequest req = (MatchChoiceRequest) request;
		MatchChoiceResponse rsp = new MatchChoiceResponse();
		ChannelFuture cf = null;
		rsp.setMatchId(req.getMatchId());
		MatchInfo match = getMatch(req.getMatchId());
		if(match == null || match.getState() != MatchState.WAIT_FOR_READY){
			rsp.setResult(ResultTypeList.PLAYER_MATHSTATE_ERROR);
			cf = ccm.sendResponse(PROTOID.MATCH_CHOICE_RSP, req.getMachineId(), rsp);
			return ResultTypeList.PLAYER_MATHSTATE_ERROR;
		}		
		if(req.getChoice() == 1){
			for (EnrollPlayerInfo player : match.getPlayerList()) {
				if(player.getMachineId() == req.getMachineId()){//判断是不是这个选手的比赛状态，通过这个判断能正确处理该处理的人
					player.setState(EnrollPlayerState.READY);
					rsp.setResult(ResultTypeList.SUCCESS);
					// rsp to incomer that the req is handled
					cf = ccm.sendResponse(PROTOID.MATCH_CHOICE_RSP, req.getMachineId(), rsp);
					if(cf == null){
						// to be finished if incomer lost connection
					}
					// ntf to everybody that the incomer's state is now ready
					sendMatchStateChangedNTF(match);
					// check if everyone else in this match is ready
					if(checkPlayerStateReached(EnrollPlayerState.READY, match)){
						match.setState(MatchState.WAIT_FOR_CONNECTION);
						sendMatchStateChangedNTF(match);
					}
					break;
				}
			}
		}else if(req.getChoice() == 0) {
			match = removeMatch(req.getMatchId());
			//rsp that the req is handled
			rsp.setResult(ResultTypeList.SUCCESS);
			cf = ccm.sendResponse(PROTOID.MATCH_CHOICE_RSP, req.getMachineId(), rsp);
			if(cf == null){
				// to be finished if incomer lost connection
			}
			// handle other players in the same match
			match.setState(MatchState.MATCH_FAILURE);
			for (EnrollPlayerInfo player : match.getPlayerList()) {
				
				//inform refused
				MatchStateChangedNTF notif = getNotifByMatch(match);
				ccm.sendResponse(PROTOID.MATCH_STATE_CHANGED_NTF, player.getMachineId(), notif);
				//没有拒绝的选手重新加入匹配列表
				if(player.getMachineId() != req.getMachineId()&&player.getIsChallenger()==0) {
					EnrollRequest request1 = new EnrollRequest();
					request1.setMachineId(player.getMachineId());
					request1.setSongId(player.getSongId());
					em.handleEnroll(PROTOID.MATCH_ENROLL_REQ, request1);
				}						
			}
		}else {
			rsp.setResult(ResultTypeList.ERROR_ARGS);
			ccm.sendResponse(PROTOID.MATCH_CHOICE_RSP, req.getMachineId(), rsp);
			return ResultTypeList.ERROR_ARGS;
		}
		return rsp.getResult();
	}

	
	public int handleP2PConnectionReady(int cmd, IRequest request) throws IllegalArgumentException, IllegalAccessException, JsonProcessingException{
		P2PConnectionReadyRequest req = (P2PConnectionReadyRequest) request;
		MatchInfo match = getMatch(req.getMatchId());
		P2PConnectionReadyResponse rsp = new P2PConnectionReadyResponse();
		ChannelFuture cf = null;
		rsp.setMatchId(req.getMatchId());
		if(match == null || match.getState() != MatchState.WAIT_FOR_CONNECTION){
			rsp.setResult(ResultTypeList.PLAYER_MATHSTATE_ERROR);
			cf = ccm.sendResponse(PROTOID.P2P_CONNECTION_READY_RSP, req.getMachineId(), rsp);
			if(cf == null){
				// to be handled in the future;	
			}
		}else {
			for (EnrollPlayerInfo player : match.getPlayerList()) {
				if(player.getMachineId() == req.getMachineId()){
					player.setState(EnrollPlayerState.P2P_CONNECTED);
					rsp.setResult(ResultTypeList.SUCCESS);
					cf = ccm.sendResponse(PROTOID.P2P_CONNECTION_READY_RSP, req.getMachineId(), rsp);
					if(cf == null){
						// to be handled in the future;
						
					}
					break;
				}
			}
			if(checkPlayerStateReached(EnrollPlayerState.P2P_CONNECTED, match)){
				match.setState(MatchState.FIGHTING);
				sendMatchStateChangedNTF(match);
				for (EnrollPlayerInfo player : match.getPlayerList()) {
					player.setState(EnrollPlayerState.FIGHTING);
				}
			}
			
		}
		return rsp.getResult();
	}
	
	public int handleFightFinish(int cmd, IRequest request) throws IllegalArgumentException, IllegalAccessException, JsonProcessingException{
		FightFinishRequest req = (FightFinishRequest) request;
		MatchInfo match = getMatch(req.getMatchId());
		ChannelFuture cf = null;
		FightFinishResponse rsp = new FightFinishResponse();
		if(req.getMatchId()==-1||null == req.getScore()) {
			rsp.setResult(ResultTypeList.NULL_ARGS);
			ccm.sendResponse(PROTOID.FIGHT_FINISH_RSP, req.getMachineId(), rsp);
			return ResultTypeList.NULL_ARGS;
		}
		rsp.setMatchId(req.getMatchId());
		if(match == null || ((match.getState() != MatchState.FIGHTING) && (match.getState() != MatchState.FIRST_ONE_FINISHED))){
			//如果选手不是在完成比赛或者第一个完成比赛状态，则表示客户端发送指令有问题
			rsp.setResult(ResultTypeList.PLAYER_MATHSTATE_ERROR);
			cf = ccm.sendResponse(PROTOID.FIGHT_FINISH_RSP, req.getMachineId(), rsp);
			if(cf == null){
				// to be handled in the future;
				
			}
		}else {
			//System.out.println("match founded");
			for (EnrollPlayerInfo player : match.getPlayerList()) {
				if(player.getMachineId() == req.getMachineId()){
					int index = match.getPlayerList().indexOf(player);
					if(match.getState() != MatchState.FIRST_ONE_FINISHED){
						match.setState(MatchState.FIRST_ONE_FINISHED);
						List<MatchScore> scoreList =  new ArrayList<MatchScore>();
						match.setScoreList(scoreList);
						for(int i = 0;i < match.getPlayerList().size();i ++){
							scoreList.add(new MatchScore());
						}
					}
					MatchScore score = match.getScoreList().get(index);
					score.copyFrom(req.getScore());
					player.setState(EnrollPlayerState.FINISHED);
					rsp.setResult(ResultTypeList.SUCCESS);
					cf = ccm.sendResponse(PROTOID.FIGHT_FINISH_RSP, req.getMachineId(), rsp);
					if(cf == null){
						// to be handled in the future;
					}
					break;
				}
			}
			//System.out.println("start check is all finished");
			if(checkPlayerStateReached(EnrollPlayerState.FINISHED, match)){//如果是第一个完全比赛的选手则不走下面了，
				//所以上面设置的player.setState(EnrollPlayerState.FINISHED);还是MatchState.FIRST_ONE_FINISHED
				//System.out.println("finishing match");
				finishMatch(match);
				//System.out.println("send match finish notif");
				sendMatchStateChangedNTF(match);
				MyFactory.context.getBean("DBService", DBService.class).
				updateWinOrDefeat(match.getPlayerList().get(match.getWinnerIndex()).getPlayerInfo().getOpenId(),
						match.getPlayerList().get(1-match.getWinnerIndex()).getPlayerInfo().getOpenId());
			}
			
		}
		return rsp.getResult();
	}
	
	//比赛结束
	private void finishMatch(MatchInfo match){
		match.setState(MatchState.FINISHED);
		for(EnrollPlayerInfo eInfo:match.getPlayerList()) {
			//pm.getGeneralPlayerInfo(eInfo.getPlayerInfo().getOpenId()).setStartTime(GeneralPlayerState.CREATED);
			pm.setPlayerState(eInfo.getPlayerInfo().getOpenId(), GeneralPlayerState.CREATED);
		}
		
		
		float maxScore = 0;
		int maxIndex = 0;
		MatchScore score = null;
		// handle match result, like the score ,etc to be finished in the future
		for (int i = 0;i < match.getScoreList().size();i ++){
			score = match.getScoreList().get(i);
			if(score.getTotal() > maxScore){
				maxScore = score.getTotal();
				maxIndex = i;
			}
		}
		match.setWinnerIndex(maxIndex);
	}
	
	//玩家离开房间
	public int handleLeaveFight(int cmd, IRequest request) throws IllegalArgumentException, IllegalAccessException, JsonProcessingException{
		LeaveFightRequest req = (LeaveFightRequest) request;
		MatchInfo match = getMatch(req.getMatchId());
		ChannelFuture cf = null;
		LeaveFightResponse rsp = new LeaveFightResponse();
		rsp.setMatchId(req.getMatchId());
		if(match == null || match.getState() != MatchState.FINISHED){
			rsp.setResult(ResultTypeList.PLAYER_MATHSTATE_ERROR);
			cf = ccm.sendResponse(PROTOID.LEAVE_FIGHT_RSP, req.getMachineId(), rsp);
			if(cf == null){
				// to be handled in the future;
				
			}
		}else {
			for (EnrollPlayerInfo player : match.getPlayerList()) {
				if(player.getMachineId() == req.getMachineId()){
					player.setState(EnrollPlayerState.LEAVED);
					rsp.setResult(ResultTypeList.SUCCESS);
					cf = ccm.sendResponse(PROTOID.LEAVE_FIGHT_RSP, req.getMachineId(), rsp);
					if(cf == null){
						// to be handled in the future;
						
					}
					break;
				}
			}
			if(checkPlayerStateReached(EnrollPlayerState.LEAVED, match)){
				match.setState(MatchState.DISMISSED);	
				removeMatch(match.getMatchId());	
				sendMatchStateChangedNTF(match);
			}
		}
		return rsp.getResult();
	}
	
	public boolean checkPlayerStateReached(int state, MatchInfo match) throws IllegalArgumentException, IllegalAccessException, JsonProcessingException{
		for (EnrollPlayerInfo player : match.getPlayerList()) {
			if(player.getState() != state){
				return false;
			}
		}
		return true;	
	}
	
	private MatchInfo insertMatchList(MatchInfo match){
		return matchList.put(match.getMatchId(), match);
	}
	
	private MatchInfo removeMatch(int matchId){
		//System.out.println("removematch:"+matchId);
		MatchInfo match = matchList.remove(matchId);
		match.setToBeRemoved(true);
		for (EnrollPlayerInfo player : match.getPlayerList()) {
			pm.setPlayerState(player.getPlayerInfo().getOpenId(), GeneralPlayerState.CREATED);
			pm.setPlayerMatchId(player.getPlayerInfo().getOpenId(), -1);
		}
		
		return match;
	}
	
	private MatchInfo changeMatchStatus(MatchInfo match){
		match.setToBeRemoved(true);
		for (EnrollPlayerInfo player : match.getPlayerList()) {
			pm.setPlayerState(player.getPlayerInfo().getOpenId(), GeneralPlayerState.CREATED);
			pm.setPlayerMatchId(player.getPlayerInfo().getOpenId(), -1);
		}
		
		return match;
	}
	
	private MatchInfo getMatch(int matchId){
		return matchList.get(matchId);
	}
	private MatchStateChangedNTF getNotifByMatch(MatchInfo match){
		MatchStateChangedNTF notif = new MatchStateChangedNTF();
		notif.setMatchId(match.getMatchId());
		notif.setMatchState(match.getState());
		notif.setPlayerInfoCount(match.getPlayerList().size());
		notif.setPlayerInfoList(match.getPlayerList());
		notif.setSongId(match.getSongId());
		notif.setStartTime(match.getStartTime());
		notif.setScoreList(match.getScoreList());
		notif.setWinnerIndex(match.getWinnerIndex());
		return notif;
	}

}
